package com.grm.workflow.service.impl;

import cn.hutool.core.util.ObjectUtil;
import com.grm.common.entity.PageResult;
import com.grm.common.exception.BusinessException;
import com.grm.common.utils.JwtUtil;
import com.grm.workflow.enums.ProcessStatusEnum;
import com.grm.workflow.mapper.WfDeployFormMapper;
import com.grm.workflow.model.WfDeployForm;
import com.grm.workflow.request.process.WfProcessQueryParam;
import com.grm.workflow.response.WfProcessResponse;
import com.grm.workflow.service.WfProcessService;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.flowable.engine.IdentityService;
import org.flowable.engine.RepositoryService;
import org.flowable.engine.RuntimeService;
import org.flowable.engine.repository.Deployment;
import org.flowable.engine.repository.ProcessDefinition;
import org.flowable.engine.repository.ProcessDefinitionQuery;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;

@Service
@Slf4j
public class WfProcessServiceImpl implements WfProcessService {
    @Autowired
    private RepositoryService repositoryService;
    @Autowired
    private WfDeployFormMapper wfDeployFormMapper;
    @Autowired
    private JwtUtil jwtUtil;
    @Resource
    protected IdentityService identityService;
    @Resource
    protected RuntimeService runtimeService;

    @Override
    public PageResult<WfProcessResponse> querySelective(Integer pageNum, Integer pageSize, WfProcessQueryParam queryParam) {
        // 流程定义列表数据查询
        ProcessDefinitionQuery processDefinitionQuery = repositoryService.createProcessDefinitionQuery()
                .latestVersion()
                .orderByProcessDefinitionKey()
                .asc();
        // 流程标识
        if (StringUtils.isNotBlank(queryParam.getKey())) {
            processDefinitionQuery.processDefinitionKeyLike("%" + queryParam.getKey() + "%");
        }
        // 流程名称
        if (StringUtils.isNotBlank(queryParam.getName())) {
            processDefinitionQuery.processDefinitionNameLike("%" + queryParam.getName() + "%");
        }
        // 流程分类
        if (StringUtils.isNotBlank(queryParam.getCategory())) {
            processDefinitionQuery.processDefinitionCategory(queryParam.getCategory());
        }
        // 流程状态: 必须激活的才能发起
        processDefinitionQuery.active();
        /*
        SELECT COUNT(*) FROM act_re_procdef WHERE ...
         */
        long pageTotal = processDefinitionQuery.count();
        if (pageTotal <= 0) {
            return new PageResult<>(List.of(), 0L);
        }
        /*
        SELECT * FROM act_re_procdef WHERE ...
         */
        List<ProcessDefinition> definitionList = processDefinitionQuery.listPage((pageNum - 1) * pageSize, pageSize);
        List<WfProcessResponse> processResponseList = new ArrayList<>(definitionList.size());
        for (ProcessDefinition processDefinition : definitionList) {
            String deploymentId = processDefinition.getDeploymentId();
            /*
            SELECT * FROM act_re_deployment WHERE id_ = ?

            SELECT pd.*, d.*
            FROM act_re_procdef pd
            JOIN act_re_deployment d ON pd.deployment_id_ = d.id_
            WHERE pd.id_ = ?
             */
            Deployment deployment = repositoryService.createDeploymentQuery().deploymentId(deploymentId).singleResult();
            // 查询表单
            WfDeployForm wfDeployForm = wfDeployFormMapper.queryByDeployId(deploymentId);
            WfProcessResponse wfProcessResponse = getWfProcessResponse(processDefinition, deployment);
            wfProcessResponse.setFormId(wfDeployForm.getFormId());
            processResponseList.add(wfProcessResponse);
        }
        return new PageResult<>(processResponseList, pageTotal);
    }

    private WfProcessResponse getWfProcessResponse(ProcessDefinition processDefinition, Deployment deployment) {
        WfProcessResponse wfProcessResponse = new WfProcessResponse();
        wfProcessResponse.setDefinitionId(processDefinition.getId());
        wfProcessResponse.setProcessKey(processDefinition.getKey());
        wfProcessResponse.setProcessName(processDefinition.getName());
        wfProcessResponse.setVersion(processDefinition.getVersion());
        wfProcessResponse.setCategory(processDefinition.getCategory());
        wfProcessResponse.setDeploymentId(processDefinition.getDeploymentId());
        wfProcessResponse.setSuspended(processDefinition.isSuspended());
        wfProcessResponse.setDeploymentTime(deployment.getDeploymentTime());
        return wfProcessResponse;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void start(String processDefId, Map<String, Object> variables) {
        ProcessDefinition processDefinition = repositoryService.createProcessDefinitionQuery()
                .processDefinitionId(processDefId).singleResult();
        if (ObjectUtil.isNotNull(processDefinition) && processDefinition.isSuspended()) {
            throw new BusinessException("流程已被挂起，请先激活流程");
        }
        // 设置流程发起人Id到流程中
        String loginUserIdStr = String.valueOf(jwtUtil.getUserId());
        identityService.setAuthenticatedUserId(loginUserIdStr);
        variables.put("initiator", loginUserIdStr);
        // 设置流程状态为进行中
        variables.put("processStatus", ProcessStatusEnum.RUNNING.getStatus());

        // 针对不同的流程分类，处理不同的业务逻辑
        String processName = processDefinition.getName();
        if(StringUtils.isBlank(processName)){
            throw new BusinessException("请定义流程名称！");
        }
        // 项目间借物
        if(processName.equals("projectABorrowProjectB")){
            // 模拟数据
            variables.put("projectAManager", 1);
            variables.put("projectBManager", 2);
        }else if(processName.equals("projectInBorrow")){
            // 模拟数据
            variables.put("projectManager", 1);
        }else{
            log.info("processName={}",processName);
        }
        // 发起流程实例
        runtimeService.startProcessInstanceById(processDefinition.getId(), variables);
    }
}
